import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

/******************************************************************************************************************
* This class encapsulates a measurement frame. It can also be given a reference to a filter to populate itself or 
* send itself through the pipe. 
* Also, this class provides pretty printing for measurement strings. 
* This class provides services on low-level data conversion.
* @author Team 10
******************************************************************************************************************/
public class Measurement 
{	
	// length of frame shared by filters
	public static int framelength=0;
	
	// Internal measurement data
	private byte[] data = new byte[framelength];
	
	// readByteStream Method
	// Populates a measurement from the filter's given port. 
	public void readByteStream(FilterFramework2 filter, int port) 
			throws FilterFramework2.EndOfStreamException
	{
		for(int i=0; i<framelength; i++)
		{
			data[i] = filter.ReadFilterInputPort(port);
		}
	}

	// writeByteStream Method
	// Send itself into the pipe of a given filter
	public void writeByteStream(FilterFramework2 filter, int port)
	{
		for(int i=0; i<framelength; i++)
		{
			filter.WriteFilterOutputPort(data[i], port);
		}
	}
	
	// toByteArrayDouble Static Method
	// Return a byte array from a double. 
	public static byte[] toByteArrayDouble(double value) {
	    byte[] bytes = new byte[8];
	    ByteBuffer.wrap(bytes).putDouble(value);
	    return bytes;
	}

	// toDouble Static Method
	// Return a double from a byte array. 
	public static double toDouble(byte[] bytes) {
	    return ByteBuffer.wrap(bytes).getDouble();
	}


	// toByteArrayLong Static Method
	// Return a byte array from a long. 	
	public static byte[] toByteArrayLong(long value) {
	    byte[] bytes = new byte[8];
	    ByteBuffer.wrap(bytes).putLong(value);
	    return bytes;
	}


	// toLong Static Method
	// Return a long from a byte array. 	
	public static long toLong(byte[] bytes) {
	    return ByteBuffer.wrap(bytes).getLong();
	}
	
	// getValue method 
	// Returns a value inside the measurement given an id. 
	public Object getValue(int id)
	{
		int startPoint = (id*12)+4;

		
		if(id==0)
		{
			// Special case for 0 - Time
			long templong = toLong(Arrays.copyOfRange(data,startPoint, startPoint+8));
			return new Long(templong);
		}
		else 
		{
			// General case - not Time
			double temp = toDouble(Arrays.copyOfRange(data, startPoint, startPoint+8));
			return new Double(temp); 
		}
		
	}
	
	// getValue method 
	// Sets a value inside the measurement given an id. 
	public void setValue(int id, Object value)
	{
		int startPoint = (id*12)+4;
		
		if(id==0)
		{
			// Special case for 0 - Time
			byte[] bytes = toByteArrayLong((Long)value);
			for(int i=0; i<8; i++)
			{
				data[i+startPoint] = bytes[i];
			} 
		}
		
		else
		{
			// General case - not Time
			byte[] bytes = toByteArrayDouble((Double)value);
			
			for(int i=0; i<8; i++)
			{
				data[i+startPoint] = bytes[i];
			} 
		}
	}
	

	// toString Method 
	// Create a string representation of its data. 
	public String toString(int[] typesToFilter) throws ClassNotFoundException, IOException
	{
		return toString(typesToFilter, -1);
	}
	

	// toString Method 
	// Create a string representation of its data. 
	public String toString(int[] typesToFilter, int asteriskId) throws ClassNotFoundException, IOException
	{
		String result = "";
		Double data;
		Date date = null;
		SimpleDateFormat dateformat = new SimpleDateFormat("YYYY:DD:HH:mm:ss");
		DecimalFormat df = new DecimalFormat("00.00000");
		
		// Assemble a string
		for(int i=0; i<typesToFilter.length; i++)
		{
			
			if(typesToFilter[i]==0)
			{
				// Special case for 0 - Time
				date = new Date(((Long)getValue(0)).longValue());//date = new Date((Long)getValue(0));
				result=result.concat(dateformat.format(date));
			}
			else
			{
				// General case for not 0 - Time
				data = (Double)getValue(typesToFilter[i]);;
				result=result.concat(df.format(data));
			}

			if(typesToFilter[i] == asteriskId)
			{
				// Handle asterisk
				result=result.concat("*");
			}
			result=result.concat("         ");
		}
		
		return result+"\r\n";
	}
	
	
	
	// equals Method
	// if any value in the dataframe is not equal to its corresponding id in the other dataframe
	// then the frames are not equal.
	public boolean equals(Object obj)
	{
		boolean retVal = true;
		Measurement other = (Measurement) obj;
		
		for(int i=0; i<(framelength/12); i++)
		{
			// Check for all internal values
			if(!this.getValue(i).equals(other.getValue(i)))
			{
				retVal = false;
				break;
			}
		}
		
		return retVal;
	}
}
